package com.zz.client;

import com.zz.client.config.ChannelCache;
import com.zz.client.config.TunnelClientConfig;
import com.zz.common.config.Config;
import com.zz.common.enums.MessageType;
import com.zz.common.exception.TunnelException;
import com.zz.client.factory.ClientBootstrapFactory;
import com.zz.common.protocol.TunnelProtocol;
import com.zz.common.tunnel.Tunnel;
import com.zz.common.tunnel.TunnelHelper;
import com.zz.common.utils.JsonUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;


@Slf4j
public class TunnelClientApplication implements Tunnel {

    private final NioEventLoopGroup workerGroup;
    private final NioEventLoopGroup localGroup;

    private final Bootstrap clientBootstrap;

    private final Bootstrap localBootstrap;

    private final TunnelClientConfig clientConfig = TunnelClientConfig.getInstance();

    private static int reconnectNum;

    public TunnelClientApplication() {
        workerGroup = new NioEventLoopGroup();
        localGroup = new NioEventLoopGroup();
        localBootstrap = ClientBootstrapFactory.createLocalBootstrap(localGroup);
        clientBootstrap = ClientBootstrapFactory.createClientBootstrap(localBootstrap, workerGroup);
    }

    @Override
    public void start() {
        connectServer();
    }


    private void connectServer() {
        clientBootstrap.connect(clientConfig.getServerBind(), clientConfig.getServerPort()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    // 连接成功，向服务器发送客户端认证信息（clientKey）
                    TunnelProtocol protocol = Config.getInstance().getProtocol().newInstance();
                    protocol.setType(MessageType.C_TYPE_AUTH.getMark());
                    protocol.setHeader(JsonUtil.genHeaderInfo(clientConfig.getClientId(), clientConfig.getClientKey()));
                    future.channel().writeAndFlush(protocol);
                    ChannelCache.setMasterServerChannel(future.channel());
                    log.info("connect proxy server success, {}", future.channel());
                } else {
                    log.warn("connect server failed", future.cause());
                    // 连接失败，发起重连
                    reconnectNum++;
                    reconnectWait();
                    connectServer();
                }
            }
        });
    }

    private void reconnectWait() {
        try {
            if (reconnectNum > 3) {
                throw new TunnelException("启动绑定端口异常！error ");
            }
            synchronized (this) {
                wait(1000);
            }
        } catch (InterruptedException e) {
            throw new TunnelException("重试异常");
        }
    }

    @Override
    public void stop() {
        workerGroup.shutdownGracefully();
    }


    public static void main(String[] args) {

        TunnelHelper.start(Arrays.asList(new Tunnel[]{new TunnelClientApplication()}));
    }
}
